/**
 * Project: ameba.biz
 * 
 * File Created at 2015年12月15日
 * $Id$
 * 
 * Copyright 2015 dface.cn Croporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * dface Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with www.dface.cn
 */
package cn.dface.ameba.biz.service.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;

import cn.dface.ameba.biz.service.StorageService;
import cn.dface.ameba.biz.vo.BucketVo;
import cn.dface.ameba.biz.vo.ResourceVo;
import cn.dface.ameba.biz.vo.UserVo;
import cn.dface.ameba.dal.dao.BucketDAO;
import cn.dface.ameba.dal.dao.ResourceDAO;
import cn.dface.ameba.dal.entity.BucketDo;
import cn.dface.ameba.dal.entity.ResourceDo;
import cn.dface.framework.core.enums.ResultEnums;
import cn.dface.framework.core.page.PageModel;
import cn.dface.framework.core.vo.ResultVo;
import cn.dface.ucenter.biz.service.ServerService;

/**
 * StorageServiceImpl
 * 
 * @author LVZY
 */
@Service
public class StorageServiceImpl implements StorageService {

    Logger                logger = Logger.getLogger(this.getClass());

    @Autowired
    private ResourceDAO   resourceDao;
    @Autowired
    private BucketDAO     bucketDao;
    
    @Autowired
    private ServerService ucenterService;

    private Auth          auth;
    private UploadManager uploadManager;
    private BucketManager bucketManager;

    private String        bucketName;

    @Autowired
    public StorageServiceImpl(@Value("${qiniu.accessKey}") String accessKey,
                              @Value("${qiniu.secretKey}") String secretKey,
                              @Value("${qiniu.bucket}") String bucketName) {
        this.auth = Auth.create(accessKey, secretKey);
        this.uploadManager = new UploadManager();
        this.bucketManager = new BucketManager(auth);
        this.bucketName = bucketName;

        logger.info(accessKey);
        logger.info(secretKey);
    }

    @Override
    public ResourceVo save(ResourceVo resource) {
        ResourceDo resDo = new ResourceDo();
        resDo.setName(resource.getName());
        resDo.setDescription(resource.getDescription());
        resDo.setKey(resource.getKey());
        resDo.setMimeType(resource.getMimeType());
        resDo.setSize(resource.getSize());
        resDo.setBucketId(resource.getBucket().getSid());
        resDo.setUserId(resource.getUser().getSid());
        this.resourceDao.add(resDo);

        ResourceDo savedResDo = this.resourceDao.findById(resDo.getSid());
        resource.setSid(savedResDo.getSid());
        resource.setUser(this.getUserFromUserConter(savedResDo.getUserId()));
        return resource;
    }

    @Override
    @Transactional(rollbackFor = { Exception.class })
    public ResultVo sendToQiniuAndSaveToDB(ResourceVo resource) {
        BucketDo bucket = this.bucketDao.findById(resource.getBucket().getSid());
        if (null == bucket) {
            return new ResultVo(ResultEnums.FAILED, "Error-001", "Bucket not found.");
        }
        String bucketName = StringUtils.hasText(this.bucketName) ? this.bucketName
                : bucket.getName();
            try {
                Response response = this.uploadManager.put(resource.getData(), null,
                        this.getUpToken(bucketName, null));
                ResourceDo resDo = response.jsonToObject(ResourceDo.class);
                resDo.setName(resource.getName());
                resDo.setDescription(resource.getDescription());
                resDo.setBucketId(resource.getBucket().getSid());
                resDo.setUserId(resource.getUser().getSid());
                this.resourceDao.add(resDo);
                
                resource.setSid(resDo.getSid());
                resource.setKey(resDo.getKey());
                resource.setMimeType(resDo.getMimeType());
                resource.setSize(resDo.getSize());
                
                return new ResultVo(resource);
            } catch (QiniuException e) {
                return new ResultVo(ResultEnums.FAILED, "Error-002", "Qiniu Exception");
            }
    }

    @Override
    public Integer countInBucket(Integer bucketId, String descKey, Integer pageSize) {
        Integer total = this.resourceDao.count(bucketId, descKey);
        if (total % pageSize > 0) {
            return total / pageSize + 1;
        } else {
            return total / pageSize;
        }
    }

    @Override
    public PageModel getPageOfResource(Integer bucketId, String descKey, Integer pageNo,
                                       Integer pageSize) {
        PageModel retModel = new PageModel();
        Map<Integer, UserVo> userMap = new HashMap<Integer, UserVo>();
        List<ResourceVo> resources = this.resourceDao
                .findByBucketIdAndPage(bucketId, descKey, (pageNo - 1) * pageSize, pageNo * pageSize)
                .stream()
                .map(resDo -> {
                    ResourceVo resVo = new ResourceVo();
                    resVo.setSid(resDo.getSid());
                    resVo.setName(resDo.getName());
                    resVo.setDescription(resDo.getDescription());
                    resVo.setKey(resDo.getKey());
                    resVo.setMimeType(resDo.getMimeType());
                    resVo.setSize(resDo.getSize());
                    resVo.setCreateAt(resDo.getCreateAt());
                    if (userMap.containsKey(resDo.getUserId())) {
                        resVo.setUser(userMap.get(resDo.getUserId()));
                    } else {
                        resVo.setUser(this.getUserFromUserConter(resDo.getUserId()));
                    }
                    return resVo;
                }).collect(Collectors.toList());
        retModel.setObjects(resources);
        Integer total = this.resourceDao.count(bucketId, descKey);
        if (total % pageSize > 0) {
            retModel.setTotalPageNo(total / pageSize + 1);
        } else {
            retModel.setTotalPageNo(total / pageSize);
        }
        return retModel;
    }

    @Override
    public BucketVo save(BucketVo bucket) {
        BucketDo bucketDo = this.transformBucket(bucket);
        this.bucketDao.add(bucketDo);
        bucket.setSid(bucketDo.getSid());
        return bucket;
    }

    @Override
    public List<BucketVo> getAllOfBucket() {
        return this.bucketDao.findAll()
                             .stream()
                             .map(bucketDo -> {
                                 return this.transformBucket(bucketDo);
                             }).collect(Collectors.toList());
    }

    @Override
    public BucketVo getOneOfBucket(Integer id) {
        return this.transformBucket(this.bucketDao.findById(id));
    }

    @Override
    public void updateBucket(BucketVo bucket) {
        this.bucketDao.update(this.transformBucket(bucket));
    }

    private String getUpToken(String bucket, String key) {
        return auth.uploadToken(bucket, key, 3600,
                new StringMap().put("returnBody", this.getReturnBody(
                        new String[] { 
                                "key", "etag", 
                                "mimeType", "mimeType", 
                                "size", "fsize", 
                            })));
    }

    private String getReturnBody(String[] kAndV) {
        StringBuilder sb = new StringBuilder("{");
        for (int i = 0; i < kAndV.length; i += 2) {
            sb.append("\"")
            .append(kAndV[i])
            .append("\": ")
            .append("$(")
            .append(kAndV[i + 1])
            .append("), ");
        }
        sb.replace(sb.length() - 2, sb.length(), "}");
        return sb.toString();
    }

    @SuppressWarnings("rawtypes")
    private UserVo getUserFromUserConter(Integer id) {
        ResultVo result = this.ucenterService.loadUserBySid(id);
        UserVo user = new UserVo();
        user.setSid(id);
        user.setName(((Map)result.getObject()).get("userName").toString());
        return user;
    }

    private BucketVo transformBucket(BucketDo bucket) {
        BucketVo ret = new BucketVo();
        ret.setSid(bucket.getSid());
        ret.setName(bucket.getName());
        ret.setDesc(bucket.getDesc());
        ret.setUrl(bucket.getUrl());
        return ret;
    }

    private BucketDo transformBucket(BucketVo bucket) {
        BucketDo ret = new BucketDo();
        ret.setSid(bucket.getSid());
        ret.setName(bucket.getName());
        ret.setDesc(bucket.getDesc());
        ret.setUrl(bucket.getUrl());
        return ret;
    }

}
